﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Data;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using Medianamik.Core.Security;
using Medianamik.Core.Sugar;

namespace Medianamik.Core
{
    //TODO: Lancer une erreur lors d'appels aux propriétés non initialisées (IsLoaded == false)
    //Exemple le Path et le Depth

    /// <summary>
    /// Classe représantant un Type Medianamik
    /// </summary>
    [DebuggerDisplay("{Name}")]
    public class NodeType : HierarchicalEntity<NodeType>
    {
        private const string DefaultIconPath = "/medianamik/images/NodeTypeIcons/Ico_Blank.png";

        /// <summary>
        /// Ce constructeur est requis pour utiliser Node avec des méthodes génériques
        /// </summary>
        public NodeType()
        {
            _legitimateParentNodes = null;
        }

        /// <summary>
        /// Constructeur permettant de créer un nouveau NodeType depuis l'extérieur du Core
        /// </summary>
        public NodeType(Guid typeId, string pName, bool pCanHaveChilds, bool pIsGenerated, bool pIsAbstract, bool pIsSystem, Guid? parentId, bool pIsManyToMany,
            List<NodeType> pLegitimateParentTypes, List<Node> pLegitimateParentNodes, List<TypeTemplate> templates)
        {
            _legitimateParentNodes = null;
            ParentID = parentId;
            ID = typeId;
            _name = pName;
            IsInheritable = pCanHaveChilds;
            IsGenerated = pIsGenerated;
            IsAdmin = true;
            IsAbstract = pIsAbstract;
            IsSystem = pIsSystem;
            IsManyToMany = pIsManyToMany;
            _legitimateParentTypes = pLegitimateParentTypes;
            _legitimateParentNodes = pLegitimateParentNodes;
            _templates = templates;

        }
        public NodeType(string pName, bool pCanHaveChilds, bool pIsGenerated, bool pIsAbstract, bool pIsSystem, Guid? parentId, bool pIsManyToMany,
            List<NodeType> pLegitimateParentTypes, List<Node> pLegitimateParentNodes, List<TypeTemplate> templates, NodeTypeExtendedProperties parentExtendedProperties)
            : this(Guid.NewGuid(), pName, pCanHaveChilds, pIsGenerated, pIsAbstract, pIsSystem, parentId, pIsManyToMany,
            pLegitimateParentTypes, pLegitimateParentNodes, templates)
        {
            if (parentExtendedProperties != null)
                ExtendedProperties = parentExtendedProperties;
        }

        /// <summary>
        /// Constructeur utile dans la création d'une liste de NodeType parents/enfants
        /// Loaded = false
        /// </summary>
        public NodeType(Guid pID)
        {
            _legitimateParentNodes = null;
            ID = pID;
        }

        /// <summary>
        /// Constructeur permettant de charger un NodeType existant depuis l'intérieur du Core
        /// </summary>
        internal NodeType(Guid pID, string pName, bool pCanHaveChilds, bool pIsGenerated, bool pIsAbstract, bool pIsSystem, bool pIsManyToMany, string pPath, int pDepth, Guid? pParentId)
            : base(pID, pParentId, pPath, pDepth)
        {
            _legitimateParentNodes = null;
            _name = pName;
            IsInheritable = pCanHaveChilds;
            IsAbstract = pIsAbstract;
            IsSystem = pIsSystem;
            IsGenerated = pIsGenerated;
            IsAdmin = true;
            IsManyToMany = pIsManyToMany;
        }
        internal NodeType(Guid pID, string pName, bool pCanHaveChilds, bool pIsGenerated, bool pIsAbstract, bool pIsSystem, bool pIsManyToMany, string pPath, int pDepth)
            : this(pID, pName, pCanHaveChilds, pIsGenerated, pIsAbstract, pIsSystem, pIsManyToMany, pPath, pDepth, null)
        {

        }

        public bool HasMultipleTemplates
        {
            get { return Templates.Count > 1; }
        }

        private string _name;
        public String Name
        {
            get
            {
                if (_name == null && Status == EntityStates.New)
                {
                    throw new CoreException("NodeType is not loaded.");
                }

                return _name;
            }
            set
            {
                _name = value;
                Status = EntityStates.Dirty;
            }
        }

        public NodeType ParentType
        {
            get
            {
                return ParentID.HasValue ? NodeTypeManager.GetNodeType(ParentID.Value) : null;
            }
        }

        public bool HasParent
        {
            get { return ParentID.HasValue && ParentID.Value != Guid.Empty; }
        }

        public bool IsSystem
        {
            get;
            set;
        }

        public bool IsAbstract
        {
            get;
            set;
        }

        public bool IsAdmin { get; set; }

        public string LocalizedName
        {
            get
            {
                return GetLocalizedName(MedianamikContext.Current.CurrentUICulture);
            }
        }

        public string GetLocalizedName(CultureInfo cultureInfo)
        {
            if (Status != EntityStates.New)
            {
                return (string)System.Web.HttpContext.GetGlobalResourceObject("TypeManagement", ID + "_TypeName", cultureInfo);
            }
            return string.Empty;
        }

        public string Description { get; set; }

        public bool IsInheritable
        {
            get;
            set;
        }

        public bool IsGenerated
        {
            get;
            set;
        }

        public bool IsDeleted { get; internal set; }

        public bool IsManyToMany
        {
            get;
            set;
        }

        bool? _isUnrestricted;
        public bool IsUnrestricted
        {
            get
            {
                if (_isUnrestricted == null)
                    _isUnrestricted = Listable || NodeTypeManager.IsUnrestricted(this);

                return _isUnrestricted.GetValueOrDefault(false);
            }
        }

        internal BitVector32 Flags;

        public bool Indexable
        {
            get
            {
                return Flags[(int)NodeTypeFlag.Indexable];
            }
            set
            {
                Flags[(int)NodeTypeFlag.Indexable] = value;
            }
        }

        public bool Listable
        {
            get
            {
                return Flags[(int)NodeTypeFlag.Listable];
            }
            set
            {
                Flags[(int)NodeTypeFlag.Listable] = value;
            }
        }

        private bool _isVersioned;
        public bool IsVersioned
        {
            get { return _isVersioned && !Listable; }
            set { _isVersioned = value; }
        }

        public int GetNodeCount()
        {
            return NodeTypeManager.TypeNodesCount(ID, true);
        }

        public int GetTotalNodeCount()
        {
            return NodeTypeManager.TypeNodesCount(ID);
        }

        public int GetInheritorsCount()
        {
            return NodeTypeManager.GetChildTypes(ID).Count;
        }

        public int GetTotalInheritorsCount()
        {
            return NodeTypeManager.GetAllChildTypes(ID).Count;
        }

        private bool? _hasChildren;
        public bool HasChildren
        {
            get
            {
                if (!_hasChildren.HasValue)
                {
                    _hasChildren = NodeTypeManager.TypeHasChildren(ID);
                }

                return _hasChildren.GetValueOrDefault(false);
            }
            internal set
            {
                _hasChildren = value;
            }
        }
        public bool HasNodes
        {
            get { return GetNodeCount() > 0; }
        }

        public bool IsParentOf(NodeType childNode)
        {
            if (childNode == null)
                return false;

            return childNode.Path.IndexOf(ID.ToString(), StringComparison.OrdinalIgnoreCase) > -1;
        }

        public bool IsChildOf(NodeType parentNode)
        {
            if (parentNode == null)
                return false;

            return Path.IndexOf(parentNode.ID.ToString(), StringComparison.OrdinalIgnoreCase) > -1;
        }
        public bool IsChildOf(Guid parentNodeId)
        {
            if (parentNodeId == Guid.Empty)
                return false;

            return Path.IndexOf(parentNodeId.ToString(), StringComparison.OrdinalIgnoreCase) > -1;
        }

        private bool? _isLanguageNeutral;
        public bool IsLanguageNeutral
        {
            get
            {
                if (_isLanguageNeutral == null)
                    _isLanguageNeutral = GetAllProperties().All(p => p.IsLanguageNeutral);

                return _isLanguageNeutral.GetValueOrDefault();
            }
        }

        private List<NodeType> _legitimateParentTypes;
        public List<NodeType> LegitimateParentTypes
        {
            get
            {
                if (_legitimateParentTypes == null)
                {
                    _legitimateParentTypes = new List<NodeType>();

                    if (!IsUnrestricted)
                        _legitimateParentTypes = ReadOnlyOriginalLegitimateParentTypes.ToList();
                }

                return _legitimateParentTypes;
            }
        }
        private List<NodeType> _originalLegitimateParentTypes;
        internal List<NodeType> OriginalLegitimateParentTypes
        {
            get
            {
                if (_originalLegitimateParentTypes == null)
                {
                    _originalLegitimateParentTypes = IsUnrestricted
                                                   ? new List<NodeType>()
                                                   : NodeTypeManager.GetLegitimateParentTypes(ID).ToList();
                }

                return _originalLegitimateParentTypes;
            }
            set
            {
                _originalLegitimateParentTypes = value;
            }
        }
        public IEnumerable<NodeType> ReadOnlyOriginalLegitimateParentTypes
        {
            get { return OriginalLegitimateParentTypes.AsEnumerable(); }
        }

        private List<Node> _legitimateParentNodes;
        public List<Node> LegitimateParentNodes
        {
            get
            {
                if (_legitimateParentNodes == null)
                {
                    _legitimateParentNodes = NodeManager.GetLegitimateParentNodes(ID);
                    _originalLegitimateParentNodes = new List<Node>(_legitimateParentNodes);
                }
                return _legitimateParentNodes;
            }
        }
        private List<Node> _originalLegitimateParentNodes;
        internal List<Node> OriginalLegitimateParentNodes
        {
            get
            {
                if (_originalLegitimateParentNodes == null)
                {
                    _originalLegitimateParentNodes = NodeManager.GetLegitimateParentNodes(ID);
                }

                return _originalLegitimateParentNodes;
            }
            set
            {
                _originalLegitimateParentNodes = value;
            }
        }
        public IEnumerable<Node> ReadOnlyOriginalLegitimateParentNodes
        {
            get { return OriginalLegitimateParentNodes.AsEnumerable(); }
        }

        private List<NodeType> _legitimateChildTypes;
        public IEnumerable<NodeType> LegitimateChildTypes
        {
            get
            {
                if (_legitimateChildTypes == null)
                {
                    _legitimateChildTypes = NodeTypeManager.GetLegitimateChildTypes(ID).ToList();
                }

                return _legitimateChildTypes;
            }
        }

        /// <summary>
        /// Collection de NodeTypeProperties liées au NodeType
        /// </summary>
        internal NodeTypePropertyCollection PropertiesInternal
        {
            get
            {
                EnsureLoadProperties();
                return _properties;
            }
        }
        public ReadOnlyCollection<NodeTypeProperty> Properties
        {
            get
            {
                return PropertiesInternal.AsReadOnly();
            }
        }
        private NodeTypePropertyCollection _properties;

        private NodeTypePropertyCollection _inheritedProperties;
        internal NodeTypePropertyCollection InheritedPropertiesInternal
        {
            get
            {
                EnsureLoadProperties();
                return _inheritedProperties;
            }
        }
        public ReadOnlyCollection<NodeTypeProperty> InheritedProperties
        {
            get
            {
                EnsureLoadProperties();
                return InheritedPropertiesInternal.AsReadOnly();
            }
        }

        private void EnsureLoadProperties()
        {
            if (_inheritedProperties == null || _properties == null)
            {
                _inheritedProperties = new NodeTypePropertyCollection(this);
                _properties = new NodeTypePropertyCollection(this);
            }
            else { return; /* Les propriétés sont déjà chargées */ }

            //On charge les propriétés mais si IsNew=true
            //Car le Type peut avoir des propriétés héritées

            var ownProperties = NodeTypeManager.GetNodeTypeProperties(ID)
                .Where(p => p.TypeID.Equals(ID));

            var inheritedProperties = Enumerable.Empty<NodeTypeProperty>();
            if (ParentID.HasValue)
            {
                inheritedProperties = NodeTypeManager.GetNodeTypeProperties(ParentID.Value)
                    .Where(p => !p.TypeID.Equals(ID));
            }

            _properties.AddRange(ownProperties);
            _inheritedProperties.AddRange(inheritedProperties);

        }

        private List<InheritedPermission> _inheritedPermission;
        public ReadOnlyCollection<InheritedPermission> InheritedPermissions
        {
            get
            {
                if (_inheritedPermission == null)
                {
                    _inheritedPermission = SecurityManager.GetInheritedNodeTypePermissionsByTypeId(ID).ToList();
                }
                return _inheritedPermission.AsReadOnly();
            }
        }

        private List<Permission> _permissions;
        public ReadOnlyCollection<Permission> Permissions
        {
            get
            {
                if (_permissions == null)
                {
                    _permissions = SecurityManager.GetNodeTypePermissionsByTypeId(ID).ToList();
                }
                return _permissions.AsReadOnly();
            }
        }

        private List<ManyToManyRelation> _manyToManyRelations;
        public ReadOnlyCollection<ManyToManyRelation> ManyToManyRelations
        {
            get
            {
                if (_manyToManyRelations == null)
                {
                    var relations = NodeTypeManager.GetManyToManyRelations(GetParentIds(true));
                    //Si le type a une relation ManyToMany sur lui-même, elle va apparaitre deux fois
                    // Il faut filtrer les relations dans ce cas
                    var filteredRelations = relations.Distinct(new ManyToManyRelationEqualityComparer());
                    _manyToManyRelations = new List<ManyToManyRelation>(filteredRelations);

                }
                return _manyToManyRelations.AsReadOnly();
            }

        }

        private List<OneToManyRelation> _primaryRelations;
        public ReadOnlyCollection<OneToManyRelation> PrimaryRelations
        {
            get
            {
                if (_primaryRelations == null)
                {
                    var relations = NodeTypeManager.GetPrimaryRelations(GetParentIds(true)).Distinct();
                    _primaryRelations = new List<OneToManyRelation>(relations);
                }
                return _primaryRelations.AsReadOnly();
            }

        }

        private List<OneToManyRelation> _foreignRelations;
        public ReadOnlyCollection<OneToManyRelation> ForeignRelations
        {
            get
            {
                if (_foreignRelations == null)
                {
                    var relations = NodeTypeManager.GetForeignRelations(GetParentIds(true)).Distinct();
                    _foreignRelations = new List<OneToManyRelation>(relations);
                }
                return _foreignRelations.AsReadOnly();
            }
        }


        private List<Breadcrumb> _breadcrumbs;
        public IEnumerable<Breadcrumb> Breadcrumbs
        {
            get
            {
                if (_breadcrumbs == null)
                {
                    _breadcrumbs = new List<Breadcrumb>();

                    List<NodeType> parentTypes = NodeTypeManager.GetParentTypes(ID, true);

                    if (parentTypes.Count > 0)
                    {
                        parentTypes.Sort((t1, t2) => t1.Depth.CompareTo(t2.Depth));
                        _breadcrumbs.AddRange(parentTypes.ConvertAll(t => new Breadcrumb(t.ID.ToString(), t.Name)));
                    }
                }

                return _breadcrumbs;
            }
        }

        private NodeTypeExtendedProperties _extendedProperties;
        public NodeTypeExtendedProperties ExtendedProperties
        {
            get
            {
                return _extendedProperties ?? (_extendedProperties = new NodeTypeExtendedProperties());
            }
            internal set { _extendedProperties = value; }
        }

        public SubEntityCollection<NodeType, NodeTypeInterfaceImplementation> Interfaces
        {
            get
            {
                EnsureLoadInterfaces();
                return _interfaces;
            }
        }
        private SubEntityCollection<NodeType, NodeTypeInterfaceImplementation> _interfaces;

        public bool RespectsInterface(NodeTypeInterface pInterface)
        {
            return pInterface.Properties.All(p =>
                GetAllProperties().Any(prop => p.IsEquivalentToTypeProperty(prop)));
        }

        public IEnumerable<string> RespectsInterfaces(IEnumerable<Guid> pInterfaceIds)
        {
            var invalidInterfaces = new List<string>();

            for (var i = 0; i < pInterfaceIds.Count(); i++)
            {
                var interf = NodeTypeManager.GetNodeTypeInterface(pInterfaceIds.ElementAt(i));
                if (!RespectsInterface(interf))
                {
                    invalidInterfaces.Add(interf.Name);
                }
            }

            return invalidInterfaces;
        }

        private List<TypeTemplate> _templates;
        public List<TypeTemplate> Templates
        {
            get
            {
                if (_templates == null)
                {
                    _templates = NodeTypeManager.GetTemplates(ID).ToList();
                    _originalTemplates = new List<TypeTemplate>(_templates);
                }
                return _templates;
            }
        }
        private List<TypeTemplate> _originalTemplates;
        internal List<TypeTemplate> OriginalTemplates
        {
            get
            {
                if (_originalTemplates == null)
                {
                    _originalTemplates = NodeTypeManager.GetTemplates(ID).ToList();
                }

                return _originalTemplates;
            }
            set
            {
                _originalTemplates = value;
            }
        }

        private string _iconPath;
        public string IconPath
        {
            get
            {
                if (_iconPath == null)
                {
                    _iconPath = (string)ExtendedProperties.Icon;
                    if (string.IsNullOrEmpty(_iconPath)
                        && ParentID.HasValue)
                    {
                        _iconPath = NodeTypeManager.GetNodeType(ParentID.Value).IconPath;
                    }

                    if (string.IsNullOrEmpty(_iconPath))
                    {
                        _iconPath = DefaultIconPath;
                    }

                    _iconPath = EnsureRelativePath(_iconPath);
                }
                return _iconPath;
            }
            set
            {
                NodeType parent = null;

                if (ParentID.HasValue)
                {
                    parent = NodeTypeManager.GetNodeType(ParentID.Value);
                }

                if (parent != null && String.Compare(parent.IconPath, value, true) == 0)
                {
                    ExtendedProperties.Icon.Value = String.Empty;
                }
                else
                {
                    ExtendedProperties.Icon.Value = value;
                }
            }
        }

        private static string EnsureRelativePath(string path)
        {
            return Uri.IsWellFormedUriString(path, UriKind.Absolute)
                            ? new Uri(path, UriKind.Absolute).PathAndQuery
                            : (path ?? string.Empty);
        }

        private void EnsureLoadInterfaces()
        {
            if (_interfaces == null)
            {
                _interfaces = new SubEntityCollection<NodeType, NodeTypeInterfaceImplementation>(this);
                _interfaces.AddRange(NodeTypeManager.GetNodeTypeInterfaces(ID));
            }
            else { return; /* Les propriétés sont déjà chargées */ }
        }

        public ReadOnlyCollection<NodeTypeProperty> GetAllProperties()
        {
            return Properties.Union(InheritedProperties).ToList().AsReadOnly();
        }

        public ReadOnlyCollection<NodeTypeProperty> GetAllPropertiesWithManyToManyRelations()
        {
            return GetAllProperties().Union(ManyToManyRelations.Select(r =>
                r.RightRelation.GetForeignProperty())).ToList().AsReadOnly();
        }

        public ReadOnlyCollection<NodeTypeProperty> GetAllRefreshedProperties()
        {
            _inheritedProperties = null;
            return Properties.Union(InheritedProperties).ToList().AsReadOnly();
        }

        public int GetPropertyPosition(Guid pPropertyId)
        {
            XElement elm = ExtendedProperties.GetPropertyPositionElement(pPropertyId);

            //La propriété n'a jamais été ordonnée
            if (elm == null)
            {
                return Int32.MaxValue;
            }

            int position = elm.ElementsBeforeSelf().Count();

            return position;
        }

        public bool PropertyIsVisible(Guid propertyId)
        {
            return ExtendedProperties.GetPropertyHiddenElement(propertyId) == null;
        }

        public void SetPropertyVisibility(Guid propertyId, bool visible)
        {
            var property = ExtendedProperties.GetPropertyHiddenElement(propertyId);

            if (visible && property != null)
                property.Remove();

            if (!visible && property == null)
                ExtendedProperties.HiddenProperties.Add(new XElement("Property", new XAttribute("Id", propertyId)));
        }

        public void MoveProperty(Guid pPropertyId, Guid pDestinationPropertyId, DropPosition pPosition)
        {
            EnsurePropertyPositions();

            var oldPositionElm = ExtendedProperties.GetPropertyPositionElement(pPropertyId);

            //On supprime l'ancien élément de la collection si il existait
            if (oldPositionElm != null) { oldPositionElm.Remove(); }

            Func<XElement, bool> destPropPredicate = (elm =>
                (Guid)elm.Attribute("Id") == pDestinationPropertyId);

            if (ExtendedProperties.PropertyPositions.Elements().Any(destPropPredicate))
            {
                var elm = ExtendedProperties.PropertyPositions.Elements().First(destPropPredicate);
                if (pPosition == DropPosition.Above)
                {
                    elm.AddBeforeSelf(new XElement("Property", new XAttribute("Id", pPropertyId)));
                }
                else
                {
                    elm.AddAfterSelf(new XElement("Property", new XAttribute("Id", pPropertyId)));
                }
            }

        }

        private void EnsurePropertyPositions()
        {
            var manyToManyProps = ManyToManyRelations.Select(r => r.RightRelation.GetForeignProperty());
            var allProps = GetAllProperties().Union(manyToManyProps).Select(p => p.ID);

            foreach (var elm in ExtendedProperties.PropertyPositions.Elements())
            {
                if (!allProps.Contains((Guid)elm.Attribute("Id"))) { elm.Remove(); }
            }

            foreach (var prop in allProps)
            {
                var guid = prop;

                if (!ExtendedProperties.PropertyPositions.Elements().Any(elm => (Guid)elm.Attribute("Id") == guid))
                {
                    ExtendedProperties.PropertyPositions.Add(new XElement("Property", new XAttribute("Id", prop)));
                }
            }
        }

        public IEnumerable<OneToManyRelation> GetOneToManyRelations()
        {
            var lookupProps = from p in GetAllProperties()
                              where p.Relationship == NodeTypePropertyRelationship.OneToMany
                              join r in ForeignRelations on p.ID equals r.ForeignPropertyID
                              select r;

            return lookupProps;
        }

        public IEnumerable<ManyToManyRelation> GetManyToManyRelations()
        {
            var manyToManyProps = from r in ManyToManyRelations
                                  where r.LeftRelation.ForeignTypeIsManyToMany
                                  select r;
            return manyToManyProps;
        }

        public NodeTypeProperty AddProperty(string pName
            , short? pMaxLength
            , short? pMinLength
            , bool pHaveTooltip
            , bool pIsLanguageNeutral
            , bool pAllowsNulls
            , bool pIsUnique
            , Regex pValidationRegex
            , string pGroupName
            , Guid? pMedianamikControlID
            , MedianamikControlInfo? pMedianamikControlSettings
            , Guid pDataTypeId
            , Guid? pPrimaryTypeID
            , Guid? pPrimaryPropertyID
            , Guid? pPrimaryMedianamikControlID
            , MedianamikControlInfo? pPrimaryMedianamikControlSettings
            , Guid? pPrimaryDataTypeId
            , Guid? pTreeRootNodeId)
        {
            var newProp = new NodeTypeProperty(pName
                , pMaxLength
                , pMinLength
                , ID
                , pHaveTooltip
                , pIsLanguageNeutral
                , pAllowsNulls
                , pIsUnique
                , pValidationRegex
                , pGroupName
                , pMedianamikControlID
                , pMedianamikControlSettings
                , pDataTypeId
                , pPrimaryTypeID
                , pPrimaryPropertyID
                , pPrimaryMedianamikControlID
                , pPrimaryMedianamikControlSettings
                , pPrimaryDataTypeId
                , pTreeRootNodeId);
            PropertiesInternal.Add(newProp);
            return newProp;
        }

        public NodeTypeProperty AddProperty(string pName
            , short? pMaxLength
            , short? pMinLength
            , bool pHaveTooltip
            , bool pIsLanguageNeutral
            , bool pAllowsNulls
            , bool pIsUnique
            , Regex pValidationRegex
            , string pGroupName
            , Guid? pMedianamikControlID
            , MedianamikControlInfo? pMedianamikControlSettings
            , Guid pDataTypeId)
        {
            return AddProperty(pName
                , pMaxLength
                , pMinLength
                , pHaveTooltip
                , pIsLanguageNeutral
                , pAllowsNulls
                , pIsUnique
                , pValidationRegex
                , pGroupName
                , pMedianamikControlID
                , pMedianamikControlSettings
                , pDataTypeId
                , null, null, null, null, null, null);
        }

        public bool RemoveProperty(string pName)
        {
            NodeTypeProperty prop = PropertiesInternal.FirstOrDefault(p => p.Name.Equals(pName, StringComparison.OrdinalIgnoreCase));

            if (prop != null)
            {
                PropertiesInternal.Remove(prop);
                return true;
            }

            //Propriété non trouvée...
            return false;
        }

        public bool RemoveProperty(Guid pId)
        {
            NodeTypeProperty prop = PropertiesInternal.FirstOrDefault(p => p.ID.Equals(pId));

            if (prop != null)
            {
                PropertiesInternal.Remove(prop);
                return true;
            }

            //Propriété non trouvée...
            return false;
        }

        public bool PropertyNameExists(string pPropertyName, out Guid pPropertyID)
        {
            NodeTypeProperty prop = GetAllProperties().FirstOrDefault(p => p.Name.Equals(pPropertyName, StringComparison.OrdinalIgnoreCase));

            if (prop != null)
            {
                pPropertyID = prop.ID;
                return true;
            }
            pPropertyID = Guid.Empty;
            return false;
        }

        public bool IsOwnProperty(string propertyName)
        {
            return Properties.Any(p => p.Name.Equals(propertyName, StringComparison.OrdinalIgnoreCase));
        }

        public List<Guid> GetParentIds(bool pInclusive, int pDepth)
        {
            return DataUtil.GetBottomToTopNodeIds(Path, pInclusive, pDepth);
        }

        public List<Guid> GetParentIds(bool pInclusive)
        {
            return GetParentIds(pInclusive, 0);
        }

        public bool Inherits(Guid pTypeId)
        {
            return (GetParentIds(true).IndexOf(pTypeId) > 0);
        }

        public bool Implements(Guid interfaceId)
        {
            return Interfaces.Any(i => i.InterfaceId == interfaceId);
        }

        public override int GetHashCode()
        {
            return ID.GetHashCode();
        }

        protected override NodeType GetParent(Guid parentId)
        {
            return NodeTypeManager.GetNodeType(parentId);
        }

        public DataTable ToDataTable()
        {
            var dataTable = new DataTable(Name);

            foreach (var property in GetAllProperties())
            {
                dataTable.Columns.Add(new DataColumn(property.Name,
                    DataUtil.GetCSharpType(property, false)));
            }

            return dataTable;
        }
    }

    [Flags]
    public enum NodeTypeFlag
    {
        Indexable = 1, /* Sera indéxé par Lucene */
        Listable = 2 /* Sera exclus du module de gestion de contenu et inclus dans le module de liste */
    }
}